perm filename INUQ.MAC[IP,SYS]1 blob sn#680214 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-INET>INUQ.MAC.40303 26-Apr-82 15:48:39, Edit by CLYNN
; Fixed logical host
; Check INTON before perform a JSYS, INTQFK has owner's FORKX
;<TAPPAN.4>INUQ.MAC.14,  8-Mar-82 14:31:37, Edit by TAPPAN
; Fix bad max-packet-size value returned in ASNIQ%
;<TAPPAN.4>INUQ.MAC.13, 26-Feb-82 13:06:22, Edit by TAPPAN
; Add ASNIQ bit AQ%ICM (1B2) which allows the Q to
; send and receive ICMP messages
;<TAPPAN.4>INUQ.MAC.6, 23-Feb-82 18:25:07, Edit by TAPPAN
; Add INQICM (process user Q ICMP messages). Allow
; user to send an ICMP message if it if about the
; correct protocal/port/etc for his Q.
; flush logical host stuff since it was fairly bogus,
; and meaningful only with the arpanet.
; on SNDIN, if a user specifies the source host (in the packet)
; and it is one of our addresses, then leave it alone.
;<TAPPAN.4>INUQ.MAC.5, 22-Feb-82 18:44:00, Edit by TAPPAN
; merge
;<403-INET>INUQ.MAC.40301 29-Jan-82 15:14:13, Edit by CLYNN
; Updated for IP release 3
; Modularized protocols
; User interface definitions moved to MONSYM, Parameters to STG
;[BBNF]<402-INET>INUQ.MAC.51, 13-Jul-81 11:48:00, Ed: CLYNN
; Fix: Port swap in INQDS2
;[BBND]<402-INET>INUQ.MAC.50, 22-Jun-81 14:43:44, Ed: TAPPAN
; AT SNDIN0+24. CAILE T2,<MINIHS+3>/4 -> CAIGE
;[BBND]<402-INET>INUQ.MAC.48, 10-Feb-81 10:09:04, Ed: CLYNN
; Don't want address in T1, want error code
;	SNDIN7		HRROI T1,[-1,,SNDIX1]	HRROI T1,SNDIX1
; No packet to return when get to SNDIN8
;	SNDIN8		SKIPA T1,[-1,,SNDIX2]	HRROI T1,SNDIX2
;						JRST SNDINX
;[BBND]SNARK:<402-INET>INUQ.MAC.47,  7-Nov-80 14:02:24

	SEARCH	INPAR,TCPPAR,PROLOG,MONSYM
IFN MNET,<SEARCH MNTPAR>
	TTITLE	INUQ
	SUBTTL 	Internet User Queues, William W. Plummer, 27Feb79

	SWAPCD

	COMMENT !

These routines implement the user interface to the Internet world.
Once assigned, an Internet queue may be used to send and receive
messages to Internet hosts.  The buffer in user space has only
a count word, an Internet header and Internet text.  The gateway
selects an appropriate hardware interface, generates the required
local header for that network and sends the packet out.


* .ASNIQ ...  3 ...... Assign Internet queue JSYS
  ASNIQ0 ...  4 ...... Guts of ASNIQ
* .RELIQ ...  7 ...... Release Internet queue JSYS
  RELIQ0 ...  7 ...... Innards of RELIQ
  REL1IQ ...  8 ...... Release packets from one Internet queue
* .SNDIN ...  9 ...... Send an Internet segment JSYS
  SNDIN0 ...  9 ...... Send packet from user
* .RCVIN ... 12 ...... Receive an Internet segment JSYS
  INQGET ... 14 ...... Get next segment off of a queue
  CHKIQ  ... 15 ...... Check user's access to queue
  LHCHK  ... 15 ...... Check non-zero logical host values
* INTDSP ... 16 ...... Dispatch incoming Internet segments
* INQPRC ... 17 ...... Dispatch packets to queues and find next run time
* INQDSP ... 18 ...... Dispatch segments to user queues
* INQCH0 ... 20 ...... Find next queue timeout time
* INQCHK ... 20 ...... Check if INQDSP is next protocol to run
* INQINI ... 21 ...... Internet queue initialization
* INTLGO ... 21 ...... Internet job logout routine

		!

; Assign Internet Queue JSYS

;T1/	Flags,,pointer to QDB
;	AQ%SCR==1B0	; B0: Use secure interface.
;	AQ%SPT==1B1	; B1: Single(local) port protocol
;	AQ%ICM==1B2	; B2: Allow sending and receiving ICMP messages
;	(Other flag bits must be 0)
;T2/	(Not currently used.  Must be 0)
;T3/	(Not currently used.  Must be 0)
; Note:	Must have Net Wiz enabled

;	ASNIQ
;Ret+1:	 Failed.  Error code in T1.  Owning job # in T2 if ASNSX2.
;Ret+2:	OK.  Internet queue handle in T1.  Max buffer count in T2.

.ASNIQ::MCENT			; Enter monitor
	SKIPN INTON		; IP initialized?
	  RETERR(ASNSX1)	; No, Cannot have queue
	MOVX T4,SC%NWZ		; Capability bit for network wizard
	TDNN T4,CAPENB		; Caller has it enabled?
	  RETERR(NTWZX1)	; No.

	DMOVE T3,T1		; Place for call via locked call
	XMOVEI T1,INTQLK	; The lock to lock
	XMOVEI T2,ASNIQ0	; Function to call
	CALL LCKCAL		; Call work routine while lock set
	JUMPL T1,IQFAIL		; Finish up and give error return
	UMOVEM T1,T1		; Pass queue handle to caller
	MOVE T2,INTXPW		; Biggest count word user can give
	MOVEI T2,-PKTELI+1(T2)	; (without fragmentation)
	UMOVEM T2,T2		; Tell him this size
	SMRETN			; Give success return

; Guts of ASNIQ JSYS

;T1/	From user
;T2/	From user
;INTQLK/locked		NOINT
;
;	CALL ASNIQ0
;Ret+1:	Always.  T1 has queue handle, or
;	If error, T1 has -1,,errror (if T1 is ASNSX2, T2 has Job #)

ASNIQ0: STACKL <<USRQDB,.IQLEN>> ; Stack space for copy of user's QDB
	LOCAL <QDB,IQ,FQ,FLGS>
	MOVEM T1,QDB
	MOVEM T1,FLGS		; Save flags

	MOVEI T3,USRQDB		; Local copy
	MOVE T2,T1		; User data block
	MOVEI T1,.IQLEN		; Length of a queue descriptor
	CALL BLTUM		; Copy from user into monitor
	MOVEI QDB,USRQDB	; Reference only our copy now

	MOVSI T1,-.IQLEN	; Set to scan the QDB
	HRR T1,QDB
	MOVEI T2,17		; Right four bits must be cleared
	ANDCAM T2,0(T1)
	AOBJN T1,.-1

	MOVX T1,<-1B15>		; Mask for single port
	TXNE FLGS,AQ%SPT	; Single port protocol?
	  ANDM T1,.IQPTM(QDB)	; Yes.  Flush comparison on foreign port

;;; Following is useless
;;;	SETCM T2,INETLB		; Real (non-logical) host bits
;;; MNET ...
;;;	LSH T2,4		; Left justify
;;;	TDNE T2,.IQSHM(QDB)	; Cannot be called logical bits by user
;;;	  JRST ASNIQS		; Since conflicts with the system

	MOVSI IQ,-NIQ		; Set to scan the queue tables
	MOVEI FQ,0		; Indicate no free slot found yet
ASNIQ1:	MOVE T1,INTQJB(IQ)	; Get owner
	CAME T1,[-1]		; Free?
	  JRST ASNIQ2		; No.  Go check for conflicts.
	SKIPN FQ		; Already know of a free slot?
	  MOVE FQ,IQ		; No so save this one.
	JRST ASNIQ8		; And loop to next queue
; Check for conflicts with already assigned queues

ASNIQ2:	MOVE T1,.IQPRM(QDB)	; Get protocol mask word from user's blk
	AND T1,INTQM0(IQ)	; Compute least specific mask
	MOVE T2,.IQPRV(QDB)	; Get value word
	XOR T2,INTQV0(IQ)	; Compare against this queue
	TDNE T1,T2		; But only in the bits that matter
	  JRST ASNIQ8		; Difference is OK.  Try next.

	MOVE T1,.IQFHM(QDB)	; Same for foreign host
	AND T1,INTQM1(IQ)
	MOVE T2,.IQFHV(QDB)
	XOR T2,INTQV1(IQ)
	TDNE T1,T2
	  JRST ASNIQ8

	MOVE T1,.IQSHM(QDB)	; Same for local host
	AND T1,INTQM2(IQ)
	MOVE T2,.IQSHV(QDB)
	XOR T2,INTQV2(IQ)
	TDNE T1,T2
	  JRST ASNIQ8

	MOVE T1,INTQJB(IQ)	; Get flags
	XOR T1,FLGS		; Compare with request
	TXNE T1,AQ%SPT		; Differ only in single/double port spec?
	  JRST ASNIQF		; Yes.  Not allowed

	MOVE T1,.IQPTM(QDB)	; Compare port word
	AND T1,INTQM3(IQ)
	MOVE T2,.IQPTV(QDB)
	XOR T2,INTQV3(IQ)
	TDNN T1,T2
	  JRST ASNIQF		; Give fail return due to conflict

; Doesn't conflict with this queue, try next

ASNIQ8:	AOBJN IQ,ASNIQ1
	JUMPE FQ,ASNIQN		; All slots full
; Assign queue to user

	MOVE T1,.IQPRM(QDB)	; Copy into queue tables
	MOVEM T1,INTQM0(FQ)
	AND T1,.IQPRV(QDB)
	MOVEM T1,INTQV0(FQ)

	MOVE T2,.IQFHM(QDB)
	MOVEM T2,INTQM1(FQ)
	AND T2,.IQFHV(QDB)
	MOVEM T2,INTQV1(FQ)

	MOVE T3,.IQSHM(QDB)
	MOVEM T3,INTQM2(FQ)
	AND T3,.IQSHV(QDB)
	MOVEM T3,INTQV2(FQ)

	MOVE T4,.IQPTM(QDB)
	MOVEM T4,INTQM3(FQ)
	AND T4,.IQPTV(QDB)
	MOVEM T4,INTQV3(FQ)

	MOVE T1,JOBNO		; Our job number
	HLL T1,FLGS		; Merge in the flags
	MOVEM T1,INTQJB(FQ)	; Say we are the owner

	CALL REL1IQ		; Flush old packets
	SETZM INTQSP(FQ)	; No messages
	MOVE T3,TODCLK		; When assigned
	MOVEM T3,INTQTM(FQ)
	MOVE T1,FORKX		; Fork ID of creator
	HRROM T1,INTQFK(FQ)	; No fork waiting,,owner's FORKX
;	fork for receive interrupt & support

	HRRZ T1,FQ		; Get the queue handle for user
	JRST ASNIQX		; Success return


;ASNIQS:TDZA T1,T1		; Return Job 0 if reserved queue
ASNIQF:	  HRRZ T1,INTQJB(IQ)	; Get job number owning this queue
	UMOVEM T1,T2		; Give it to user
	SKIPA T1,[ASNSX2]	; "Queue already in use"
ASNIQN:	  MOVEI T1,ASNSX1	; "All queues in use"
	HRROS T1		; Make neg. left half to signal error
ASNIQX:	RESTORE
	RET
; Internet logout routine -- called by each job as it logs out

INTLGO::SETO T1,		; Say all Internet Queues
	RELIQ			; Release them
	 JFCL
	RET

; Release Internet Queue JSYS

;T1/	Internet Queue Handle or -1 for all owned by this job or
;	(multiple) fork handle for all owned by requested fork(s).
;	RELIQ
;Ret+1:	 Failure.  Error code in T1
;Ret+2:	Success

.RELIQ::MCENT			; Enter monitor
	SKIPN INTON		; IP Initialized?
	  RETERR(SQX2)		; No, can't be assigned
	MOVE T3,T1		; Place arg for LCKCAL
	AOSE T1			; Check for -1
	  JUMP [HRRZ T1,T3	; No, check RH only  ??bug here??
		CAIL T1,NIQ	; Is it a queue handle?
		  JRST RELIQ5	; No, go check for fork handle
		JRST .+1]	; Yes, back to normal code
	XMOVEI T1,INTQLK	; The lock to lock
	XMOVEI T2,RELIQ0	; Function to call
	CALL LCKCAL
	JUMPL T1,IQFAIL
	SMRETN

; Check for multiple fork handle argument

RELIQ5:	CAIGE T1,.FHJOB		; Multiple handle?
	  JRST [CAIL T1,.FHSLF	; No, Job relative handle?
		 CAIL T1,.FHSLF+NLFKS
		  JRST RELIQ7	; Neither, garbage
		JRST .+1]	; Job relative handle, continue
IFKL <	CALL FLOCK		; Lock fork structure
	MOVX T2,<CALL RLIQFK>	; Call this routine
	CALL MAPFKH		; Per fork
	  NOP			; Never blocks
	CALL FUNLK		; Unlock fork structure
> ; End of IFKL
IFKA <	CALL MAPFKH		; For each fork
	  CALL RLIQFK		; Call this routine
> ; End of IFKA
	SMRETN			; All done

RELIQ7:	HRRZ T1,SQX1		; Bad IQ handle error
	JRST MRETNE

; Lock INTQLK and do work per fork;  LCKCAL of INTQLK needs to be here
; so MAPFKH won't ITRAP with INTQLK locked.

RLIQFK:	MOVE T3,T1		; Place FORKN for LCKCAL
	XMOVEI T1,INTQLK	; The lock to lock
	XMOVEI T2,RLIQF0	; Function to call
	CALL LCKCAL
	RET

; Work routine for RLIQFK
; Called with T1 a FORKN & INTQLK locked

RLIQF0:	LOCAL <IQ,FKX>		; Get FORKX corresponding
	HRRZ FKX,SYSFK(T1)	; to MAPFKH's FORKN
	MOVSI IQ,-NIQ		; Scan all queues
RLIQF1:	MOVE T1,IQ		; Current handle
	CALL CHKIQ		; Check access
	JUMPL T1,RLIQFX		; Not this job
	HRRZ T1,INTQFK(IQ)	; Get owning FORKX
	CAME T1,FKX		; Owned by this fork?
	  JRST RLIQFX		; No, skip it
	HRRZ T1,IQ		; Yes, get queue handle
	CALL REL1IQ		; Release it and
	SETOM INTQJB(IQ)	; Deassign it
	SETZM INTQSP(IQ)	; Make sure no messages
	MOVE T1,TODCLK		; Record when queue
	MOVEM T1,INTQTM(IQ)	; was deassigned
RLIQFX:	AOBJN IQ,RLIQF1		; Loop through all queues
	RESTORE
	RET

; Innards of RELIQ

;T1/	IQ handle or -1 for all
;INTQLK/set		NOINT
;
;	CALL RELIQ0
;Ret+1:	Always.  T1 ge 0 if successful or -1,,errorcode if not

RELIQ0:	LOCAL <IQ>
	MOVEM T1,IQ
	AOSE T1			; Asked to do all for this job? (-1)
	 TLOA IQ,-1		; No.  Set AOBJN ptr to do just one
	  MOVSI IQ,-NIQ		; Yes.  Set up for all
RELIQ2:	MOVE T1,IQ		; Get the handle
	CALL CHKIQ		; Check access.
	JUMPL T1,RELIQ8		; Jump if no access (different job)

	HRRZ T1,IQ
	CALL REL1IQ		; Flush packets in this queue
	SETOM INTQJB(IQ)	; Deassign the queue
	SETZM INTQSP(IQ)	; Make sure no messages
	MOVE T1,TODCLK		; Record when queue
	MOVEM T1,INTQTM(IQ)	; was deassigned

RELIQ8:	AOBJN IQ,RELIQ2

RELIQ9:	SETZ T1,		; Always successful
	RESTORE
	RET


; Routine to flush packets in queue if it is owned by calling job

;T1/	Internet Queue handle
;INTQLK/Set		NOINT
;
;	CALL REL1IQ
;Ret+1:	Always.

REL1IQ:	LOCAL <IQ>
	MOVEM T1,IQ
	HRRZ T2,INTQJB(IQ)	; Which job owns this one
	CAME T2,JOBNO		; Us?
	  JRST REL1IX		; No.

REL1I1:	HRRZ T1,IQ		; Get the handle
	CALL INQGET		; Get a message if possible
	JUMPL T1,REL1IX		; Jump if queue now empty.
	CALL RETBLK		; Return the storage to free area
	JRST REL1I1		; Loop til queue empty

REL1IX:	RESTORE
	RET

; Send an Internet Segment JSYS

;T1/	Flags,,Internet Queue Handle (No flags defined.  Must be 0)
;T2/	Buffer Address -> <words, inc this>,<IP header>,<IP data>
;T3/	Local net address for source route
;
;	SNDIN
;Ret+1:	 Failed.  Error code in T1.
;Ret+2:	Success.

.SNDIN::MCENT			; Enter monitor
	CALL SNDIN0		; Do the work
	JUMPL T1,IQFAIL
	SMRETN


; Workhorse for SNDIN:

SNDIN0:	LOCAL <IQ,BUF,SIZ>
	NOINT
	DMOVEM T1,IQ
	XCTU [HRRZ SIZ,0(BUF)]	; Get size of user's buffer area

	HRRZ T1,IQ		; The queue handle
	CALL CHKIQ		; See if we have access to it
	JUMPL T1,SNDINX		; Jump if not
; Not need this with fragmentation
	HRROI T1,SNDIX1		; Anticipate fail return
	MOVE T2,INTXPW
	SUBI T2,PKTELI-1
	CAILE SIZ,<<MINIHS+3>/4> ; Must have size word & minimal IP header
	 CAMLE SIZ,T2		; Must fit in our biggest packets
	  JRST SNDINX		; Give fail return

	MOVEI T1,PKTELI-1(SIZ)	; Size of buffer needed here
	CALL GETBLK		; Get a chunk of free storage
	SKIPG PKT,T1		; OK?
	  JRST SNDIN8

	SETZM PKTFLG(PKT)	; Clear all internal flags
	MOVEI T1,-1(SIZ)	; Number of words in user's area
	MOVEI T2,1(BUF)		; First address in user's area
	XMOVEI T3,PKTELI(PKT)	; First address in monitor area
	CALL BLTUM		; Move it into the monitor

	LOAD T1,PIPL,(PKT)	; Packet length in bytes
	ADDI T1,3		; Set to round up
	ASH T1,-2		; Number of words which must be present
	CAIL T1,0(SIZ)		; Must be in what we were given
	  JRST SNDIN9		; No.  Length error

	LOAD T2,PIDO,(PKT)	; Get data offset in 32-bit words
	CAIGE T2,<MINIHS+3>/4	; Must be at least a full header
	  JRST SNDIN9

	SKIPE INTQM3(IQ)	; Filtering on ports?
	 CAIGE T2,0(T1)		; Yes.  Pkt must include ports.
	  CAILE T2,0(T1)	; Pkt must always include min header
	   JRST SNDIN9

	LOAD T1,PIVER,(PKT)	; Pick up the Internet version
	CAIE T1,.INTVR		; Is that right?
	  JRST SNDIN9

	LOAD T1,PIPRO,(PKT)	; Protocol
	CAIN T1,.ICMFM		; ICMP?
	 JRST SNDINC		; Yes, treat specially
	LSH T1,↑D4		; 'left justify' for TSTIQ

	LOAD T4,PIDO,(PKT)	; Get Internet data offset
	ADD T4,PKT		; Get address of port word
	MOVE T4,PKTELI(T4)	; Get the port word
	JRST SNDIN3

;;; Here if the user is sending an ICMP packet, it must be for
;;; the appropriate protocal/host/port
;;; We check the protocal and ports in the packets "error header"
;;; rather than those in the main header

SNDINC:	MOVE T2,INTQJB(IQ)	; Get flags
	TXNN T2,AQ%ICM		; ICMP messages enabled?
	  JRST SNDIN7		; Return an error
	LOAD T2,PIDO,(PKT)	; Get data offset
	ADD T2,PKT		; Add it in
	ADDI T2,.CMINH		; Plus header offset in packet
	LOAD T1,PIPRO,(T2)	; get the protocal from the header
	LSH T1,4		; Shift for TSTIQ

	LOAD T4,PIDO,(T2)	; get length from header
	ADD T4,T2		; Add in
	MOVE T4,PKTELI(T4)	; Get port word
SNDIN3:
	MOVE T2,PKTELI+.IPKSH(PKT) ; Get source
	MOVE T3,PKTELI+.IPKDH(PKT) ; and dest

	CALL TSTIQ		; All good for this Q?
	  JRST SNDIN9		; No, error.

	LOAD T1,PISH,(PKT)	; Get packet source
	JUMPE T1,SNDIN4		; User did not specifiy
IFN MNET,<CALL LCLHST>		; Is it one of us?
IFE MNET,<CAME T1,INETID>	; Is it us?
	   JRST SNDIN9		; Bad address
	JRST SNDIN5		; Skip

SNDIN4:	MOVE T1,INETID		; Default source for packet
	STOR T1,PISH,(PKT)	; Save

SNDIN5:

	MOVE T1,INTQJB(IQ)	; Get flags
	TXNN T1,AQ%SCR		; RPI desired?
	  JRST SNDIN6		; No
	SETONE PSCR,(PKT)	; Flag the packet for that interface
SNDIN6:
	XCTU [SKIPN T3]		; Source route address in T3?
	  JRST SNDINS		; No, go send
	SETONE PSROU,(PKT)	; Flag to do source routing
SNDINS:
	CALL SNDGAT		; Send it.  Low lvl code will return stg
	SETZ T1,		; Tell caller all is ok
	JRST SNDINX

; Error returns

SNDIN7:	HRROI T1,SNDIX1		; Header or format problem: SIZ, PIPL,
	JRST SNDINE		; PIDO, ports, PIVER, PIPRO, S, D

SNDIN8: HRROI T1,SNDIX2		; No storage error
	JRST SNDINX		; Don't return what we didn't get

SNDIN9:	HRROI T1,SNDIX4
SNDINE:	PUSH P,T1
	CALL RETPKT		; Return storage used for packet
	POP P,T1


SNDINX:	OKINT
	RESTORE
	RET

; Receive an Internet Segment JSYS

;T1/	Flags,,Internet Queue Handle
;		RIQ%NW On to give error return instead of waiting
;T2/	Buffer pointer -> <act,,max, inc this>,<IP header>,<IP data>
;T3/	(Not currently used.  Must be 0)
;
;	RCVIN
;Ret+1:	 Failed.  Code in T1.
;Ret+2:	Success.

.RCVIN::MCENT			; Enter the monitor
	CALL RCVIN0		; Do work
	CAMN T1,[-1]		; Nothing waiting error (RIQ%NW was 1)
	  JRST MRETNE		; Return -1
	JUMPL T1,IQFAIL		; Return 0,,errorcode
	SMRETN			; No error return

; Guts of RCVIN

;	CALL RCVIN0
;Ret+1:	Always, T1 is: -1, .lt. 0, or .ge. 0


RCVIN0:	LOCAL <IQ,BUF>
RCVIN1:	NOINT
	UMOVE IQ,T1		; Get the queue handle & flags
	UMOVE BUF,T2		; And user's buffer address
	HRRZ T1,IQ		; Get the handle
	CALL CHKIQ		; See if we have access to it
	JUMPL T1,RCVINX		; Jump if not (T1 has error code)
	HRRZ T1,IQ
	CALL INQGET		; Try to get a segment from that queue
	JUMPGE T1,RCVIN2	; Jump if we did

	TXNE IQ,RIQ%NW		; Check the "don't wait" flag
	  JRST RCVINX		; Don't wait, return -1 (from INQGET)
	MOVE T1,FORKX		; Must wait.  Get our fork number
	HRLM T1,INTQFK(IQ)	; Leave it for when segment arrives
	OKINT
	XMOVEI T1,INTQFK(IQ)	; The cell to wait on
	CALL DISL		; Wait for it to be gronked
	JRST RCVIN1		; Go try again

; Still NOINT

RCVIN2:	MOVE PKT,T1		; Put packet pointer in proper place
	LOAD T1,PIPL,(PKT)	; Get the Internet pkt length in bytes
	ADDI T1,4+3		; Allow for bfr length word.  Round up.
	ASH T1,-2		; Number of words required.
	XCTU [HRRZ T2,0(BUF)]	; Get user's buffer size
	XCTU [HRLM T1,0(BUF)]	; Tell him what's needed/present
	SETZ IQ,		; Assume no truncation error
	CAMG T1,T2		; Enough space available?
	  JRST RCVIN3		; Yes
	MOVE T1,T2		; No.  Give him what we can.
	HRROI IQ,SNDIX1		; Remember to give error return
RCVIN3:
	SOSG T1			; Don't count the count word
	  JRST RCVIN4		; User area too small
	XMOVEI T2,PKTELI(PKT)	; First word in Internet part
	MOVEI T3,1(BUF)		; First word in user's data area
	CALL BLTMU		; Transfer it to the user
RCVIN4:
	OKINT
	CALL RETPKT		; Return the packet storage
	SKIPA T1,IQ		; Error code
RCVINX:	  OKINT			; For fatal error returns
	RESTORE
	RET			; No.

; Get a message (Internet segment) from specified queue

;T1/	Internet Queue Handle
;INTQLK/ Locked		NOINT
;
;	CALL INQGET
;Ret+1:	Always.  T1 has pointer to message if any, or is -1 if none

INQGET:	LOCAL <IQ>
	MOVEM T1,IQ

	NOSKED			; Prevent simultaneous access to Q
	ADD T1,INTQHD		; Compute address of queue head
	MOVE T2,T1
	LOAD T1,QNEXT,(T2)	; Get first thing on queue
	CAIN T1,0(T2)		; If that is the queue head itself,
	  JRST INQGE9		; The queue is empty
	SETSEC T1,INTSEC	; Make extended address
	CALL DQ			; Dequeue the message and return in T1
	SOSA INTQSP(IQ)		; Credit space to queue
				; Success return. T1 has the message
INQGE9:	  SETO T1,		; Fail return.
	OKSKED
	MOVE T2,TODCLK		; "Now"
	SKIPLE INTQSP(IQ)	; Messages remaining?
	  ADD T2,INTQT0		; Yes, Bump no-activity time out
;	SKIPL T1
	  MOVEM T2,INTQTM(IQ)	; Otherwise record when examined
	RESTORE
	RET
; Check that the calling job has access an Internet Queue

;T1/	Internet Queue Handle
;			NOINT
;	CALL CHKIQ
;Ret+1:	Always.  T1 ge 0 if ok, -1,,error code if not

CHKIQ:	HRRZS T2,T1
	CAIL T2,NIQ		; Range check the handle
	  JRST CHKIQ9		; Bad
	HRRZ T2,INTQJB(T2)	; Get JOBNO which owns this queue
	CAMN T2,JOBNO		; Is that this job?
	  RET			; Yes.  T1 still has the Q index
	SKIPA T1,[-1,,SQX2]	; Owned by some other job
CHKIQ9:	  HRROI T1,SQX1		; Bad handle
	RET

; Common exit when T1 has -1,,error code

IQFAIL:	HRRZS T1
	JRST MRETNE


; LHCHK - Check non-zero logical host values

; T1/	Masked logical host value
; T2/	Protocol (preserved of bashed if for user queue)
; P1/	Interface (if MNET)
;	CALL LHCHK
; Return+1	Always, T2 changed if destined for user queue

LHCHK:	MOVE T3,T1		; Masked value (non-zero)
IFN MNET,<MOVE T4,NTNLHM(P1)>	; Mask
IFE MNET,<MOVE T4,INETLB>
; Know logical host must be in rightmost 24 bits
;	TDNN T4,[037777,,777777]
;	  LSHC T3,-40		; Right justify mask & value
	TRNN T4,177777
	  LSHC T3,-20
	TRNN T4,377
	  LSHC T3,-10
	TRNN T4,17
	  LSHC T3,-4
	TRNN T4,3
	  LSHC T3,-2
	TRNN T4,1
	  LSHC T3,-1
	CAMG T3,INTLHX		; Deliver to protocol or user queue?
	  RET			; Protocol
	LSH T3,10
	IOR T2,T3		; Logical Host,Protocol
	RET			; User - Make protocol compares fail

; Dispatch messages from the gateway to the right Internet Queue

;	(No args)
;
;	CALL INTDSP
;Ret+1:	Always.  Internet Input Queue empty.

INTDSP::
IFN MNET,<SAVP1>
	LOCAL <PIX,PTB,PTL>
	PUSH P,PKT

INTDS0:	CALL RCVGAT		; Get a message from the gateway
	JUMPE PKT,INTDSX	; None available.  We're done for now.

	XMOVEI PTB,INTPIX+1	; Locate tables
	MOVE PIX,-1(PTB)	; # Protocols
	HRRZ PTL,PIX		; Table length
	LOAD T2,PIPRO,(PKT)	; Internet protocol

;;; Kludge for handling CHAOS packets
IFDEF CHAINT,<
	CAIE T2,.CHAFM		; Chaos protocal?
	 JRST INTDS2		; No, join below
	CALL CHIINT		; give packet to protocal
	CALL RETPKT		; Give back storage
	JRST INTDS0		; and look for more
>

	LOAD T1,PIDH,(PKT)	; Find interface for the address
	CALL NETNCT		; by which we were addressed
	 TDZA T1,T1		; None ???
	  AND T1,NTNLHM(P1)	; Logical host mask for dest's net
	SKIPE T1		; Logical host 0 uses system protocols
	  CALL LHCHK		; Check non-zero logical host values

INTDS2:	SKIPN .INTPO(PTB)	; Protocol on?
	  JRST INTDS3		; No
	SKIPL T3,.INTPL(PTB)	; Take any protocol
	 CAMN T2,T3		; or match
	  JRST INTDS4		; Yes
INTDS3:
	ADD PTB,PTL		; Not for this protocol module
	AOBJN PIX,INTDS2	; Try next

; Shouldn't get here because user queues should take anything

	MOVX T1,PT%KIP		; Invalid protocol
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKI		; Yes
	MOVX T1,<DU%PRO,,ICM%DU> ; Protocol unreachable
	CALL ICMERR		; (releases storage)
	JRST INTDS0		; and process next

; Found protocol

INTDS4:	MOVE T1,PKT		; What to enqueue
	MOVE T2,.INTPQ(PTB)	; Where to enqueue it
	NOSKED			; In case protocol run by different fork
	CALL NQ
	OKSKED
	AOS .INTPF(PTB)		; Say a pkt waiting
;why?	AOS INTFLG		; And keep this fork running
	JFCL

	MOVX T1,PT%IQP		; Packet given to protocol
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKI		; Yes
	JRST INTDS0		; Process another message

INTDSX:	POP P,PKT
	RESTORE
	RET

; Internet User Queue Process:
;	Dispatch Internet messages to a user-assigned queue
;	Find next run time (when a queue timesout)

;
;	CALL INQPRC
;Ret+1:	Always. Packets queued to user or flushed; INQTIM set

INQPRC::PUSH P,PKT		; For subroutines
	SETZM INQFLG		; No rerun required
	SKIPN INQON		; Been initialized?
	  JRST INQPRX		; No??

; Pass out all packets

INQPR0:	MOVE T2,INQIPQ		; Locate Queue head
	NOSKED
	LOAD T1,QNEXT,(T2)	; Get first thing
	CAIN T1,(T2)		; Empty?
	  JRST INQPR2		; Yes
	SETSEC T1,INTSEC
	CALL DQ
	OKSKED

	MOVE T3,T1		; Arg for that routine is packet
	XMOVEI T1,INTQLK	; Internet queue lock
	XMOVEI T2,INQDSP	; Routine to call
	CALL LCKCAL		; Call routine while lock set

	JRST INQPR0		; Get rest


INQPR2:	OKSKED
	XMOVEI T1,INTQLK	; Lock to lock
	XMOVEI T2,INQCH0	; Function to call
	CALL LCKCAL
	MOVEM T1,INQTIM		; Next timeout

INQPRX:	POP P,PKT
	RET

;T1/	Pointer to packet
;INTQLK/set		NOINT
;
;	CALL INQDSP		; do work for INQPRC
;Ret+1:	Always.  Packet queued or flushed.

INQDSP:	ACVAR <IQ,IQERR>
	MOVE PKT,T1
	MOVX IQERR,<DU%PRO,,ICM%DU>	; Assume no protocol

	MOVSI IQ,-NIQ		; Set to scan them
INQDS2:	MOVE T1,INTQJB(IQ)	; Get owner of queue
	CAMN T1,[-1]		; Assigned?
	  JRST INQDS5		; No.  Go try next.

	TXNN T1,AQ%SCR		; Secure queue?
	 TDZA T1,T1		; No
	  MOVX T1,1		; Yes
	LOAD T2,PSCR,(PKT)	; Get interface class (secure or not)
	CAME T1,T2		; Packet class matches queue?
	  JRST INQDS5		; No.  Try next.

	LOAD T1,PIPRO,(PKT)	; Get Internet Protocol number
	LSH T1,↑D4
	XOR T1,INTQV0(IQ)	; Compare
	TDNE T1,INTQM0(IQ)	; But only in bits that matter
	  JRST INQDS5		; Not for this queue

	MOVE T2,PKTELI+.IPKSH(PKT) ; Source address
	XOR T2,INTQV1(IQ)
	TDNE T2,INTQM1(IQ)
	  JRST INQDS5

	LOAD T1,PIDH,(PKT)	; Destination address
	LSH T1,↑D4		; Position 32-bit wise
	XOR T1,INTQV2(IQ)	; Compare with source logical host
	TDNE T1,INTQM2(IQ)	; In the bits which matter
	  JRST INQDS5

	MOVE T1,INTQJB(IQ)	; Get flags
	LOAD T3,PIPL,(PKT)	; Get packet length in bytes
	LOAD T4,PIDO,(PKT)	; Get IN hdr length in words
	ASH T4,2		; Convert to bytes
	SKIPN INTQM3(IQ)	; Filtering on ports?
	  JRST INQDS3		; No, have length required
	ADDI T4,4		; 2 Ports take 4 more bytes
	TXNE T1,AQ%SPT		; But a single port only
	  SUBI T4,2		; Take 2 bytes
INQDS3:	CAMGE T3,T4		; Enough in packet?
	  JRST INQDS5		; Maybe more luck on a different queue

	SKIPN INTQM3(IQ)	; Should we do the port compare?
	  JRST INQDS6		; No.  We found the right queue
	MOVX IQERR,<DU%PRT,,ICM%DU> ; Port Unreachable
	LOAD T4,PIDO,(PKT)	; Get Internet Data Offset
	ADD T4,PKT		; Add base of packet
	MOVE T3,PKTELI(T4)	; Get FP.LP and 4 extra bits
	LDB T4,[POINT 16,T3,15]	; Save foreign port
	TXNN T1,AQ%SPT		; Single port protocol? FP.FP
	  LSH T3,↑D16		; No.  Move Local port  LP.FP
	DPB T4,[POINT 16,T3,31]	; Plop in the foreign port
	XOR T3,INTQV3(IQ)
	TDNE T3,INTQM3(IQ)

INQDS5:	  AOBJN IQ,INQDS2
	JUMPGE IQ,INQDS9	; Flush it if no queue found


INQDS6:	MOVE T1,INTQSP(IQ)	; Number of messages on this queue
	CAML T1,INTQMX		; Less than number allowed?
	  JRST INQDS8		; No.  Flush this one.
	AOS INTQSP(IQ)		; Count space
	MOVE T3,TODCLK		; "Now"
	ADD T3,INTQT0		; Deadman timeout
	HRRZ T2,IQ		; Queue index
	ADD T2,INTQHD		; Get pointer to the queue head
	LOAD T1,QNEXT,(T2)	; Get first thing on queue
	CAIN T1,0(T2)		; If the head itself, queue is empty.
	  MOVEM T3,INTQTM(IQ)	; Keep away the grim reaper
	MOVE T1,PKT		; What to enqueue.  T2 has where
	NOSKED
	CALL NQ
	OKSKED
	PUSH P,7		; Protect critical AC
	SKIPGE 7,INTQFK(IQ)	; See if a fork is waiting on this queue
	  JRST INQDS7		; No
	HRROS INTQFK(IQ)	; Forget that and make its wait complete
IFKA <	CALL PRWAKE>		; Get scheduler to run him now (NOINT)
INQDS7:	POP P,7
	JRST INQDSX		; Try for another segment

; Errors

INQDS8:	MOVX IQERR,ICM%SQ	; Source Quench
INQDS9:	MOVX T1,PT%UKQ
	CAIN IQERR,ICM%SQ	; Was error Source Quench?
	  MOVX T1,PT%UKS	; Yes
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKI		; Yes
	MOVE T1,IQERR		; Report error
	CALL ICMERR		; (Releases storage)
INQDSX:	RET
PURGE IQ,IQERR

;;; Give an ICMP message to a user Q
;;; PKT/ message


INQICM::XMOVEI T1,INTQLK	; Point to lock
	XMOVEI T2,INQIC0	; Routine to call
	CALL LCKCAL		; Lock and call the routine
	RETSKP			; (packet taken or flushed)


;;; Workhorse routine

INQIC0:	ACVAR <IQ,IQERR,CPKT>	; IQ and IQERR must match that at INQDSP
	LOAD CPKT,PIDO,(PKT)	; Get packet data offset
	ADD CPKT,PKT		; Add in to get to ICMP packet
	ADDI CPKT,.CMINH	; And point to the INET header therein
	MOVSI IQ,-NIQ		; Number of user Q's
INQIC1:	MOVE T2,INTQJB(IQ)
	TXNE T2,AQ%ICM		; ICMP messages allowed on this Q?
	 CAMN T2,[-1]		; Is this Q in use?
	  JRST INQIC5		; No
;;; Should we check for secure here???

	LOAD T1,PIPRO,(CPKT)	; Get protocal
	LSH T1,4		; Shift
	MOVE T2,PKTELI+.IPKSH(CPKT)	; Source host
	MOVE T3,PKTELI+.IPKDH(CPKT)	; Destination host
	LOAD T4,PIDO,(CPKT)	; Get data offset
	ADD T4,CPKT		; add in
	MOVE T4,PKTELI(T4)	; and get port word
	CALL TSTIQ		; Match this Q?
	 CAIA			; No
	  CALLRET INQDS6	; Join above to put packet on Q

INQIC5:	AOBJN IQ,INQIC1		; Loop through
	CALL RETPKT		; Nothing matches, flush
	RET			; and return

;;; TSTIQ -- Check that parameters match a special Q
;;; Called
;;; T1/	Protocal
;;; T2/ Source host
;;; T3/ destination host
;;; T4/ port word
;;; (all left justified)
;;; (This is only valid for testing packets that we are sending
;;; or have sent), expects IQ to be in Q1
;;; Returns +2 if the paremeters match the given Q


TSTIQ:	XOR T1,INTQV0(IQ)	; Check
	TDNE T1,INTQM0(IQ)	; Match?
	 RET			; no
	XOR T2,INTQV2(IQ)	; source
	TDNE T2,INTQM2(IQ)	; ?
	 RET
	XOR T3,INTQV1(IQ)	; dest
	TDNE T3,INTQM1(IQ)	; ?
	 RET
	XOR T4,INTQV3(IQ)	; Ports
	TDNE T4,INTQM3(IQ)	; ?
	 RET
	RETSKP			; match

PURGE IQ,IQERR,CPKT	
;;;	ENDAV.

; Find next timeout over all queues, and flush timedout packets

;INQLCK/ Locked		NOINT
;
;	CALL INQCH0
;Ret+1:	Always. Minimum time in T1

INQCH0:	LOCAL <IQ,TOD,NXT>
	MOVX NXT,<377777777777>
	MOVE TOD,TODCLK		; "Now"

	MOVSI IQ,-NIQ		; Set to scan all queues
INQCH1:	MOVE T1,INTQJB(IQ)	; Get owner
	CAME T1,[-1]		; Is this queue assigned?
	 SKIPG INTQSP(IQ)	; And have received packets?
	  JRST INQCH9		; No.  Try next.
	CAMG TOD,INTQTM(IQ)	; Has user forgotten it?
	  JRST INQCH8		; No

; Forgotten queue gets flushed

INQCH2:	HRRZ T1,IQ		; Get the queue handle
	CALL INQGET		; Get a message from that queue
	JUMPL T1,INQCH7		; Jump if none left
	MOVE PKT,T1		; For trace
	MOVX T1,PT%UKT		; Delivery time exceeded
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKI		; Yes
	MOVX T1,<TE%TTL,ICM%TE>	; Report Error
	CALL ICMERR		; (releases storage)
	JRST INQCH2		; Loop over the whole queue

INQCH7:	SETZM INTQSP(IQ)	; Better be zero if all flushed
	MOVE T1,TODCLK
	MOVEM T1,INTQTM(IQ)	; Remember when flushed

	JRST INQCH9


INQCH8:	CAMLE NXT,INTQTM(IQ)	; Min next check against timeout
	  MOVE NXT,INTQTM(IQ)

INQCH9:	AOBJN IQ,INQCH1		; Loop over all queues
	MOVE T1,NXT		; Value to return
	RESTORE
	RET

; See if user queues are next thing to go

;T1/	TODCLK of when next check is needed by checkers run so far
;
;	CALL INQCHK
;Ret+1:	Always.  T1 has TODCLK when to check next.

INQCHK::SKIPN INQON
	  RET
	CAMLE T1,INQTIM		; Clock to check
	  MOVE T1,INQTIM	; Next time something timesout
	RET

; Internet Queue Initialization:

INQINI::LOCAL <IQ>
	MOVX T1,<377777777777>
	MOVEM T1,INQTIM		; Don't need to be run
	SETZM INQFLG
	SETOM INQPCL		; Protocol -1 to accept anything

	MOVX T1,QSZ		; Get Input queue head
	CALL GETBLK
	JUMPLE T1,INQIN0	; Lose
	CALL INITQ		; Initialize it
	MOVEM T1,INQIPQ		; Set pointer to it

	MOVEI T1,NIQ		; Number of queues
	CALL GETBLK		; Get a block of free storage for heads
	JUMPLE T1,INQIN0	; No space
	NOSKED
	MOVEM T1,INTQHD		; Save pointer to the area

	MOVSI IQ,-NIQ		; Set to scan the queue heads
INQIN1:	HRRZ T1,IQ		; Current index
	ADD T1,INTQHD		; Plus base is the queue head
	CALL INITQ		; Initialize that queue
	SETOM INTQJB(IQ)	; Say queue not owned
	SETZM INTQTM(IQ)
	AOBJN IQ,INQIN1		; Loop through all

	XMOVEI T1,INTQLK	; Pointer to the lock
	CALL CLRLCK		; Initialize it
	OKSKED
	SETOM INQON		; All set
	CAIA
INQIN0:	  INBUG(HLT,<INQINI: Freestorage gone?>,INGWA1)
	RESTORE
	RET


	TNXEND
	END